/* 
 * File:   RotationDetector.cpp
 * Author: Guilherme Monteiro
 * 
 */

#include "../headers/RotationCorrector.h"


RotationCorrector::RotationCorrector() {
}

RotationCorrector::RotationCorrector(const RotationCorrector& orig) {
}

RotationCorrector::~RotationCorrector() {
}

/**
 * Detect the orientation of an image.
 * Uses the moments of the image to
 * calculate the angle of rotation.
 * 
 * @param img: source grayscale image
 * @return image with rotation corrected
 */
IplImage* RotationCorrector::correctsRotation(IplImage* img) {
    angleOfRotation = calculateOrientation(img);
    
    //printf("\nangle of rotation: %.2f\n\n", -angleOfRotation);
    
    // inverts the angle to correct the image
    ImageRotor* rotor = new ImageRotor();
    IplImage* newImage = rotor->rotate(img, -angleOfRotation);
    
    return newImage;
}

/**
 * Returns the angle of rotation detected
 * for the algorithm.
 * 
 * @return angle of rotation
 */
double RotationCorrector::getAngleOfRotation() {
    return angleOfRotation;
}

///**
// * Calculates the image orientation, using moments.
// * 
// * @param img: source grayscale image
// * @return angle of rotation, i.e., the image orientation
// */
//double RotationCorrector::calculateOrientation(IplImage* img) { 
////    double centralMoment_0_0 = centralMoment(img, 0, 0);
////    double mi_line_2_0 = centralMoment(img, 2, 0)/centralMoment_0_0;
////    double mi_line_0_2 = centralMoment(img, 0, 2)/centralMoment_0_0; 
////    double mi_line_1_1 = centralMoment(img, 1, 1)/centralMoment_0_0;
//    
//    CvMoments* moments;
//    
//    double orientation = 0;
//    double centralMoment_0_0 = cvGetCentralMoment(moments, 0, 0);
//    
//    if(centralMoment_0_0 != 0) {
//        double mi_line_2_0 = cvGetCentralMoment(moments, 2, 0)/centralMoment_0_0;
//        double mi_line_0_2 = cvGetCentralMoment(moments, 0, 2)/centralMoment_0_0; 
//        double mi_line_1_1 = cvGetCentralMoment(moments, 1, 1)/centralMoment_0_0;
//        
////        printf("centralMoment_0_0: %.2f\n", centralMoment_0_0);
////        printf("mi_line_0_2: %.2f\n", mi_line_0_2);
////        printf("mi_line_2_0: %.2f\n", mi_line_2_0);
////        printf("mi_line_1_1: %.2f\n", mi_line_1_1);
//        
//        if(mi_line_1_1 != 0)
//           orientation = 1/2 * atan((2*mi_line_1_1) / (mi_line_2_0 - mi_line_0_2));
//    }
//    
//    return orientation;
//}

/**
 * Calculates the image orientation, using moments.
 * 
 * @param img: source grayscale image
 * @return angle of rotation, i.e., the image orientation
 */
double RotationCorrector::calculateOrientation(IplImage* img) {
//    CvMoments* moments = (CvMoments*) malloc(sizeof(CvMoments));
//    cvMoments(img, moments, 1);
//    
//    double x_bar = moments->m10/moments->m00;
//    double y_bar = moments->m01/moments->m00; 
//    
//    printf("moments->m00: %.2f\n", moments->m00);
//    printf("(moments->m11/moments->m00): %.2f\n", (moments->m11/moments->m00));
//    printf("(x_bar * y_bar): %.2f\n", (x_bar * y_bar));
//    
//    double mi_line_2_0 = (moments->m20/moments->m00) - pow(x_bar, 2); 
//    double mi_line_0_2 = (moments->m02/moments->m00) - pow(y_bar, 2); 
//    double mi_line_1_1 = (moments->m11/moments->m00) - (x_bar * y_bar);   
    
//------------------------------------------------------------------------------
//    double moment_0_0 = moment(img, 0, 0);
//    
//    double x_bar = moment(img, 1, 0)/moment_0_0;
//    double y_bar = moment(img, 0, 1)/moment_0_0; 
//    
//    double mi_line_2_0 = (moment(img, 2, 0)/moment_0_0) - pow(x_bar, 2); 
//    double mi_line_0_2 = (moment(img, 0, 2)/moment_0_0) - pow(y_bar, 2); 
//    double mi_line_1_1 = (moment(img, 1, 1)/moment_0_0) - x_bar*y_bar;   
//    
//    printf("moment_0_0: %.2f\n", moment_0_0);
//
//------------------------------------------------------------------------------
    double centralMoment_0_0 = centralMoment(img, 0, 0);
    
//    printf("central moment 0 0: %.2f\n", centralMoment_0_0);
    
    double mi_line_2_0 = centralMoment(img, 2, 0)/centralMoment_0_0; 
    double mi_line_0_2 = centralMoment(img, 0, 2)/centralMoment_0_0; 
    double mi_line_1_1 = centralMoment(img, 1, 1)/centralMoment_0_0;   
    
    double orientation = 0;
    
    if(mi_line_1_1 != 0)
        orientation = (180.0/M_PI) * atan(2*mi_line_1_1/(mi_line_2_0 - mi_line_0_2))/2;
    else {
        //printf("mi_line_1_1 equal 0\n");
        exit(0);
    }
    
    return orientation;
}

/**
 * Returns the moment p,q-th of
 * the image.
 * 
 * @param p
 * @param q
 * @return the moment p,q associated with the image
 */
double RotationCorrector::moment(IplImage* img, int p, int q) {
    double moment = 0;
    for(int y = 0; y < img->height; y++) { // travels the image discovering the frequency of each color
        uchar* ptr2 = (uchar*)(img->imageData + y*img->widthStep); // line of pixels
        for(int x = 0; x < img->width; x++) {
//            printf("ptr2[x]: %d\n", (unsigned char)(ptr2[x]/255));
//            printf("pow(%d,%d): %.2f\n", x,p,pow(x, p));
//            printf("pow(%d,%d): %.2f\n", y,q,pow(y, q));
            moment += pow(x, p) * pow(y, q) * (unsigned char)(ptr2[x]/255);
        }
    }
    
//    printf("final moment: %.2f\n", moment);
    return moment;
}

/**
 * Returns the central moment
 * p,q-th of the image.
 * 
 * @param img: src image
 * @param p
 * @param q
 * @return central moment
 */
double RotationCorrector::centralMoment(IplImage* img, int p, int q) {
    double centralMoment = 0;
    
    double moment_0_0 = moment(img, 0, 0);
    
    double x_bar = moment(img, 1, 0)/moment_0_0;
    double y_bar = moment(img, 0, 1)/moment_0_0;
    
    for(int y = 0; y < img->height; y++) {
        uchar* ptr2 = (uchar*)(img->imageData + y*img->widthStep); // line of pixels
        for(int x = 0; x < img->width; x++) {
//            printf("ptr2[x]: %d\n", (unsigned char)(ptr2[x]/255));
//            printf("pow(%d,%d): %.2f\n", x,p,pow(x, p));
//            printf("pow(%d,%d): %.2f\n", y,q,pow(y, q));
            centralMoment += pow((x-x_bar), p) * pow((y-y_bar), q) * (unsigned char)(ptr2[x]/255.0);
        }
    }
    
//    printf("final central moment: %.2f\n", centralMoment);
    return centralMoment;
}